/**
 *
 */
package com.gs.practice.thread.util;

import java.awt.Graphics;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JPanel;

/**
 * @author Sabuj Das | sabuj.das@gmail.com
 *
 */
public class CircularArrayQueue<T>{

    private volatile Lock rwLock = new ReentrantLock();
    private volatile Condition emptyCond = rwLock.newCondition();
    private volatile Condition fullCond = rwLock.newCondition();

    private final int size;

    private final Object[] buffer;
    private volatile int front;
    private volatile int rare;

    /**
     * @param size
     */
    public CircularArrayQueue(int size) {
        this.size = size;
        this.buffer = new Object[size];
        this.front = -1;
        this.rare = -1;

    }

    public boolean isEmpty() {
        return front == -1;
    }

    public boolean isFull() {
        return (front == 0 && rare == size - 1) || (front == rare + 1);
    }

    public int enqueue(T item) {
        try {
            // get a write lock
            rwLock.lock();
            // if the Q is full, wait the write lock
            if (isFull()) {
                fullCond.await();
            }

            if (rare == -1) {
                rare = 0;
                front = 0;
            } else if (rare == size - 1) {
                rare = 0;
            } else {
                rare++;
            }

            buffer[rare] = item;
			System.out.println("Added\t: " + item);

            // notify the reader
            emptyCond.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // unlock the write lock
            rwLock.unlock();
        }
        return rare;
    }

    public T dequeue() {
        T item = null;
        try {
            // get the read lock
            rwLock.lock();
            // if the Q is empty, wait the read lock
            if (isEmpty()) {
                emptyCond.await();
            }

            item = (T) buffer[front];
            //System.out.println("Deleted\t: " + item);
            if (front == rare) {
                front = rare = -1;
            } else if (front == size - 1) {
                front = 0;
            } else {
                front++;
            }

            // notify the writer
            fullCond.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // unlock read lock
            rwLock.unlock();
        }
        return item;
    }

    public int getRare(){
        try {
            // get the read lock
            rwLock.lock();
            return rare;
        } finally {
            // unlock read lock
            rwLock.unlock();
        }
        
    }
    
    public int getFront(){
        try {
            // get the read lock
            rwLock.lock();
            return front;
        } finally {
            // unlock read lock
            rwLock.unlock();
        }
        
    }
    
    public int length(){
        try {
            // get the read lock
            rwLock.lock();
            if(isEmpty())
                return 0;
            return Math.abs(rare - front + 1);
        } finally {
            // unlock read lock
            rwLock.unlock();
        }
        
    }
    
            
    public int capacity(){
        return size;
    }
}
